/***********************************
* Name: AsmCodeGen.java
* Date: Oct 15, 2010
* @author martin
* Description:
***********************************/

package asmCodeGen;

import interCodeGen.*;
import decaf.*;
import java.util.LinkedList;

public class AsmCodeGen {

    private LinkedList<MethodInterCodeGen> interCode_list = new LinkedList<MethodInterCodeGen>();
    private DecafParser parser;
    private Scope scope;


    private LinkedList<AsmCode> code = new LinkedList<AsmCode>();

    
    public AsmCodeGen(LinkedList<MethodInterCodeGen> interCode_list){
        this.interCode_list = interCode_list;
        this.parser = this.interCode_list.getFirst().parser;
        this.scope = this.interCode_list.getFirst().scope.getFather();
    }

    /***********************************
     * generate
     ***********************************/
    public void generate(){
        
        //generar estático inicial
        this.genInicial();

        this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain(""));
        
        //generar por método
        
        for(MethodInterCodeGen a : this.interCode_list){

            MethodAsmCodeGen b = new MethodAsmCodeGen(a);
            b.generate();
            
            this.code.addAll(b.getCode());
            
            this.addToCode(new CodePlain(""));
            this.addToCode(new CodePlain(""));
            this.addToCode(new CodePlain(""));

        }
        
        //generar estático final
        this.genFinal();

    }

    /***********************************
     * @return the code
     ***********************************/
    public LinkedList<AsmCode> getCode() {
        return code;
    }

    /***********************************
     * addToCode
     * @param code to add
     ***********************************/
    public void addToCode(AsmCode line){
        this.code.add(line);
    }

    /***********************************
     * genInicial
     ***********************************/
    private void genInicial() {


        this.addToCode(new CodeComment("UNIVERSIDAD DEL VALLE DE GUATEMALA"));
        this.addToCode(new CodeComment("CONSTRUCCION DE COMPILADORES"));
        this.addToCode(new CodeComment("MARTIN GUZMAN - 08041"));
        this.addToCode(new CodeComment("PROYECTO FINAL"));
        this.addToCode(new CodePlain(""));

        this.addToCode(new CodePlain(".MODEL"+"\t"+"LARGE"));
        //this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain(".STACK"+"\t"+"5000h"));
        //this.addToCode(new CodePlain(""));
        this.addToCode(new CodeComment("",false,'-',40));
        this.addToCode(new CodePlain(".DATA"));
        this.addToCode(new CodePlain(""));


        int cant_varglobal = 0;
        for(Symbol a : this.parser.getSymbolTable().getAllGlobalSymbol(scope)){
            cant_varglobal+=a.getType().getType_length();
        }
        cant_varglobal=cant_varglobal/2;

        if(cant_varglobal!=0){
            this.addToCode(new CodePlain("GP"+"\t"+"DW"+"\t"+cant_varglobal+" DUP(0)"+"\t"+"; ALL GLOBAL VAR"));
        }
        
        this.addToCode(new CodePlain("TO_PRINT"+"\t"+"DB"+"\t"+"5 DUP(0), '$'"+"\t"+"; NUMBER TO PRINT"));
        this.addToCode(new CodePlain("ENTRAR"+"\t"+"DB"+"\t"+"0DH, 0AH, '$'"));

        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeComment("",false,'-',40));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain(".CODE"));
        this.addToCode(new CodePlain(""));

        this.genStaticMethods();

    }

    /***********************************
     * genFinal
     ***********************************/
    private void genFinal() {
        this.addToCode(new CodeComment("",false,'-',40));
        //this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain("END"+"\t"+"START"));
    }
    
    /***********************************
     * genStaticMethods
     ***********************************/
    private void genStaticMethods() {

        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeComment("",false,'#',60));
        genStartProcedure();
        this.addToCode(new CodeComment("",false,'#',60));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeComment("",false,'#',60));
        genPrintProcedure();
        this.addToCode(new CodeComment("",false,'#',60));
        this.addToCode(new CodePlain(""));


    }

    /***********************************
     * genPrintProcedure
     ***********************************/
    private void genPrintProcedure() {
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain("PRINT"+"\t"+"PROC"+"\t"+"NEAR"));
        this.addToCode(new CodePlain(""));

        this.addToCode(new CodeComment(" corregir direccion ret ",true,'-',30));
        this.addToCode(new CodeOperacionUnaria("POP","DX"));
        this.addToCode(new CodeOperacionBinaria("MOV","BX","SP"));
        this.addToCode(new CodeOperacionBinaria("MOV","[SS:BX+2]","DX"));
        this.addToCode(new CodeComment("",true,'-',30));

        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeOperacionBinaria("MOV","BX","SP"));
        this.addToCode(new CodeOperacionBinaria("MOV","BX","[SS:BX+0]"));
        this.addToCode(new CodePlain(""));

        this.addToCode(new CodeOperacionBinaria("MOV","DL","10d"));
        this.addToCode(new CodeOperacionBinaria("MOV","CX","5d"));
        this.addToCode(new CodeOperacionBinaria("MOV","DI","4d"));
        this.addToCode(new CodeLabel("PRI$1"));
        this.addToCode(new CodeOperacionBinaria("XOR","AX","AX"));
        this.addToCode(new CodeOperacionBinaria("MOV","AX","BX"));
        this.addToCode(new CodeOperacionUnaria("DIV","DL"));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeOperacionBinaria("ADD","AH","48d"));
        this.addToCode(new CodeOperacionBinaria("MOV","[TO_PRINT+DI]","AH"));
        this.addToCode(new CodeOperacionBinaria("XOR","AH","AH"));
        this.addToCode(new CodeOperacionBinaria("MOV","BX","AX"));
        this.addToCode(new CodeOperacionBinaria("ADD","BX","0d"));
        this.addToCode(new CodeJump("JZ","PRI$2"));
        this.addToCode(new CodeOperacionUnaria("DEC","DI"));
        this.addToCode(new CodeJump("LOOP","PRI$1"));
        this.addToCode(new CodeLabel("PRI$2"));
        this.addToCode(new CodeOperacionBinaria("LEA","DX","[TO_PRINT+DI]"));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeOperacionBinaria("MOV","AH","09h"));
        this.addToCode(new CodeOperacionUnaria("INT","21H"));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeOperacionBinaria("LEA","DX","[ENTRAR]"));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeOperacionBinaria("MOV","AH","09h"));
        this.addToCode(new CodeOperacionUnaria("INT","21H"));
        this.addToCode(new CodePlain(""));

        this.addToCode(new CodeComment("POP DEL REGISTRO DE ACTIVACION"));
        this.addToCode(new CodeOperacionUnaria("POP","DX"));
        //this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain("RET",true));
        this.addToCode(new CodePlain("PRINT"+"\t"+"ENDP"));
    }


    /***********************************
     * genStartProcedure
     ***********************************/
    private void genStartProcedure() {
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain("START"+"\t"+"PROC"+"\t"+"NEAR"));
        this.addToCode(new CodePlain(".STARTUP",true));
        this.addToCode(new CodeOperacionBinaria("XOR","AX","AX"));
        this.addToCode(new CodeOperacionBinaria("XOR","BX","BX"));
        this.addToCode(new CodeOperacionBinaria("XOR","CX","CX"));
        this.addToCode(new CodeOperacionBinaria("XOR","DX","DX"));
        this.addToCode(new CodePlain(""));


        //llamada al método main

        Method met = this.parser.getMethodTable().getMethod("main()");

        //----------------------------------------------------------------------
        //----------------------------------------------------------------------
        this.addToCode(new CodeComment(" METHOD CALL ",true,'-',40));

        if(met.tam_val_ret!=0){
            this.addToCode(new CodeOperacionUnaria("PUSH","0d",true,"ESPACIO PARA VALOR DE RETORNO"));
        }

        this.addToCode(new CodeOperacionUnaria("PUSH","0d",true,"ESPACIO PARA DIRECCION DE RETORNO"));

        for(int i=0;i<met.tam_temporales/2;i++){
            this.addToCode(new CodeOperacionUnaria("PUSH","0d",true,"ESPACIO PARA t"+(met.tam_temporales/2-i)));
        }

        this.addToCode(new CodeComment("ESPACIO PARA DATOS LOCALES - "+(met.tam_datos_locales/2),true));
        for(int i=0;i<met.tam_datos_locales/2;i++){
            this.addToCode(new CodeOperacionUnaria("PUSH","0d"));
        }

        this.addToCode(new CodeComment("ESPACIO PARA PARAMETROS - "+(met.tam_datos_locales/2),true));

        this.addToCode(new CodeOperacionUnaria("CALL",met.getSubname()));

        if(met.tam_val_ret!=0){
            this.addToCode(new CodeOperacionUnaria("POP","DX",true,"SACAR VALOR DE RETORNO"));
        }

        this.addToCode(new CodeComment("",true,'-',40));
        //----------------------------------------------------------------------
        //----------------------------------------------------------------------

        this.addToCode(new CodePlain(""));
        this.addToCode(new CodeOperacionBinaria("MOV","AH","4Ch"));
        this.addToCode(new CodeOperacionUnaria("INT","21h"));
        this.addToCode(new CodePlain(""));
        this.addToCode(new CodePlain("START"+"\t"+"ENDP"));
    }
    
}
